package ru.ksu.ktk.diploma.sdi.analyzer.contingency;

import java.util.*;

/**
 * Compute contingency between vectors with nominal data
 * 
 * @author coldflame 29.04.2009 20:32:25
 */
public class ContingencyResolver {
    /**
     * contingency matrix
     */
    private int[][] matrix;
    /**
     * size of first vector
     */
    private int firstDimensionSize;
    /**
     * size of second vector
     */
    private int secondDimensionSize;
    /**
     * vector of first nominal values' count
     */
    private int[] firstCountVector;
    /**
     * vector of second nominal values' count
     */
    private int[] secondCountVector;
    /**
     * total count of nominal values
     */
    private int totalCount;

    /**
     * @param firstVector first vector of nominal values
     * @param secondVector second vector of nominal values
     * @param nominalDataList
     * @return
     */
    public int[][] initContingencyMatrix(List<String> firstVector, List<String> secondVector,
        List<NominalData> nominalDataList) {
        if (firstVector == null || secondVector == null)
            throw new IllegalArgumentException("given vectors cannot be null");
        if (firstVector.isEmpty() || secondVector.isEmpty())
            throw new IllegalArgumentException("given vectors cannot be empty");

        firstDimensionSize = firstVector.size();
        secondDimensionSize = secondVector.size();
        matrix = new int[firstDimensionSize][secondDimensionSize];

        for (int i = 0; i < firstDimensionSize; i++)
            for (int j = 0; j < secondDimensionSize; j++){
                matrix[i][j] = 0;
            }
        for (NominalData nominalData : nominalDataList){
            String firstNominalValue = nominalData.getFirstNominalValue();
            int firstIndex = firstVector.indexOf(firstNominalValue);
            String secondNominalValue = nominalData.getSecondNominalValue();
            int secondIndex = secondVector.indexOf(secondNominalValue);
            if (firstIndex != -1 && secondIndex != -1){
                matrix[firstIndex][secondIndex] = nominalData.getCount();
            }
        }
        computeSums();
        return matrix;
    }

    /**
     * compute squared contingency coefficient using chi-square criteria (contingency matrix should be initialized)
     * 
     * @see ContingencyResolver#initContingencyMatrix(List, List, List)
     * @return
     */
    public float computeSquaredContingencyCoefficient() {
        if (matrix == null) throw new IllegalStateException("contingency matrix is not initialized");
        float sum = 0;
        for (int i = 0; i < firstDimensionSize; i++)
            for (int j = 0; j < secondDimensionSize; j++){
                sum += ((float) matrix[i][j] * matrix[i][j]) / (firstCountVector[i] * secondCountVector[j]);
            }
        return totalCount * (sum - 1);
    }

    /**
     * compute squared contingency coefficient using chi-square criteria
     * 
     * @param isYatesCorrected should use Yates Correction
     * @return
     */
    public float computeSquaredContingencyCoefficient(boolean isYatesCorrected) {
        if (isYatesCorrected){
            float correctionSum = 0;
            for (int i = 0; i < firstDimensionSize; i++)
                for (int j = 0; j < secondDimensionSize; j++){
                    float difference = matrix[i][j] - ((float) firstCountVector[i] * secondCountVector[j]) / totalCount;
                    float numerator = Math.abs(difference) - 0.5f;
                    correctionSum += numerator * numerator / (firstCountVector[i] * secondCountVector[j]);
                }

            return totalCount * correctionSum;
        }
        else{
            return computeSquaredContingencyCoefficient();
        }
    }

    private void computeSums() {
        firstCountVector = new int[firstDimensionSize];
        for (int i = 0; i < firstDimensionSize; i++){
            firstCountVector[i] = 0;
            for (int j = 0; j < secondDimensionSize; j++){
                firstCountVector[i] += matrix[i][j];
            }
        }
        secondCountVector = new int[secondDimensionSize];
        for (int j = 0; j < secondDimensionSize; j++){
            secondCountVector[j] = 0;
            for (int i = 0; i < firstDimensionSize; i++){
                secondCountVector[j] += matrix[i][j];
            }
        }
        for (int i = 0; i < firstDimensionSize; i++){
            totalCount += firstCountVector[i];
        }
    }

}
